「CI 全部綠燈、vsce package 也順利打包出 .vsix,這樣應該可以發版了吧?」
如果你的專案是一個純後端函式庫,這句話大致成立。但如果你的專案是一個有 UI 呈現層的工具——像 VS Code 擴充套件這種需要在編輯器裡畫出圖示、狀態、顏色的東西——「邏輯測試通過」跟「畫面顯示正確」是兩件可能完全脫鉤的事。今天用一個真實案例,講清楚這個落差怎麼一路混過測試,直到有使用者截圖回報才被發現。
這個專案的 issue #420 記錄了一個真實回報:當一個測試檔案裡的每一個測試都失敗時,VS Code 的 Test Results 面板裡,這個檔案的標題列(suite header)從測試一開始執行,就顯示綠色的 PASS 徽章,而且直到全部測試跑完、每一個子測試都顯示紅色的 × 失敗記號,這個標題列的徽章依然停留在綠色 PASS。
回報者附上了完整的診斷:問題出在畫面渲染的邏輯——標題列的徽章是在 testSuiteStarted(套件開始執行)這個時間點就被寫死設成「passed」樣式,而不是等到 testSuiteFinished(套件執行完畢)之後,根據所有子測試的實際結果重新計算。這兩個事件名稱本身是 TeamCity Service Messages 協定的命名慣例,底層傳給擴充套件的應該就是這套格式的訊息流(issue 原文沒有明講「TeamCity」這個詞,是根據事件命名慣例推測)。也就是說,這個徽章從設計上就只有「開始」這個狀態會被畫出來,沒有「根據結果更新」這個步驟。
這正是這篇要講的核心:邏輯測試沒有錯,因為畫面渲染邏輯本身根本沒有被單元測試覆蓋到——它是一段直接操作 UI 元件狀態的程式碼,而不是一段可以獨立斷言輸入輸出的商業邏輯。 如果你只看「測試套件跑不跑得過」這一個指標,這種問題完全不會被攔下來,因為壓根沒有一個測試案例在驗證「套件失敗時,畫面上的徽章顏色應該變成什麼」。
用一組對照來看這個落差:
❌ 只驗證邏輯層,沒有驗證呈現層:
單元測試:驗證 TeamCity 訊息解析函式,
輸入 testFailed 事件,輸出正確的內部狀態物件
→ 這個測試會綠燈,因為狀態物件確實被正確更新了
→ 但沒有任何測試驗證「這個狀態物件,最後有沒有被畫成正確的顏色」
中間那一段「狀態 → 畫面」的渲染邏輯完全是視覺黑箱
✅ 額外要求:呈現層變更要有對應的視覺驗證項目
發版前檢查清單多問一句:
「這次改動如果牽涉到 UI 狀態顯示,
有沒有人真的在編輯器裡跑過一次、親眼看過畫面?」
→ 不是要求每個 UI 狀態都寫自動化視覺回歸測試(成本可能過高),
但至少要求「牽涉呈現邏輯的改動,發版前手動跑一次確認畫面」
這個動作被明確列進檢查清單,而不是被邏輯測試綠燈取代
如果讓 AI 判斷「這次改動能不能發版」,它天然依賴的訊號是 CI 的通過與否——而 CI 綠燈只代表『被測試覆蓋到的邏輯』是對的,不代表『使用者實際看到的畫面』是對的。 這正好呼應這個系列反覆強調的分工:AI 擅長回答「這個已知的檢查項目通過了嗎」,但「這次改動有沒有牽涉到一段測試覆蓋不到的呈現邏輯,需要額外人工確認」這個判斷,需要對這個專案的架構(哪些部分是純邏輯、哪些部分是直接操作 UI 的視覺程式碼)有足夠的理解,才能主動提出來,而不是被動等 CI 燈號告訴你答案。
具體可執行的做法是:把「這個專案裡有哪幾類程式碼是測試覆蓋不到的視覺呈現邏輯」明確列成一份清單(例如這個專案的 Test Explorer 圖示/徽章邏輯),發版前的檢查流程裡,只要這次改動觸及清單上的檔案,就要求多加一道「手動跑一次、實際看畫面」的步驟——把「AI 判斷不出來的部分」,轉換成「AI 可以判斷『這次有沒有觸及清單』」的機械檢查。
回想你維護的專案:有沒有哪一類程式碼是「單元測試邏輯正確,但畫面/輸出呈現可能是錯的」這種測試覆蓋不到的黑箱?你的發版檢查清單裡,有沒有明確要求這類改動要多一道人工確認?
明天要換一個維度:多平台/多環境測試——Docker、SSH、Laravel Sail 這幾種常見的開發環境組合,怎麼設計測試矩陣,才不會讓某個平台限定的行為悄悄漏網。
案例真實性說明:本篇引用的 issue #420 為真實回報,內容(症狀描述、底層 TeamCity 訊息流、根因推測)忠於 gh issue view 查到的原始內容;根因分析(testSuiteStarted 時寫死樣式、未在 testSuiteFinished 重新計算)為回報者本人在 issue 中的推測,已在文中如實呈現為回報者的分析,非官方最終結論。修法對照範例中的檢查清單設計為基於這個案例性質的示範建議,非專案官方公告的正式流程。